/*
 * Copyright (c) 2007 IJO Technologies Ltd.
 * www.ijotechnologies.com
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * IJO Technologies ("Confidential Information").
 * You shall not disclose such Confidential Information and shall use
 * it only in accordance with the terms of the license agreement you
 * entered into with IJO Technologies.
 */
package com.nervenets.general.utils;


import org.joda.time.*;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Calendar;
import java.util.Date;


public final class JodaUtils {

    public static long getTimestamp() {
        return System.currentTimeMillis();
    }

    public static long getTimestamp(DateTime dateTime) {
        return dateTime.getMillis();
    }

    public static Date parseDate(long timestamp) {
        return new DateTime(timestamp).toDate();
    }

    public static String timeLongToString() {
        return timeLongToString("yyyy-MM-dd HH:mm:ss");
    }

    public static String timeLongToString(String pattern) {
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        return sdf.format(new Date());
    }

    public static String timeLongToString(long time) {
        return timeLongToString(time, "yyyy-MM-dd HH:mm:ss");
    }

    public static String timeLongToString(long time, String pattern) {
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        return sdf.format(new Date(time));
    }

    public static LocalDateTime now() {
        return LocalDateTime.now(ZoneOffset.ofHours(8));
    }

    public static long parseDateTime(String dateAsText, String pattern) throws ParseException {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
        Date date = simpleDateFormat.parse(dateAsText);
        return date.getTime();
    }

    public static boolean isToday(long timestamp) {
        return timestamp >= JodaUtils.getStartOfToday() && timestamp <= JodaUtils.getEndOfToday();
    }

    public static Integer secondsBetween(long olderTimestamp, long newerTimestamp) {
        return Seconds.secondsBetween(parseDateTime(olderTimestamp), parseDateTime(newerTimestamp)).getSeconds();
    }

    public static Integer secondsBetween(LocalDate older, LocalDate newer) {
        return Seconds.secondsBetween(older, newer).getSeconds();
    }

    public static Integer minutesBetween(long olderTimestamp, long newerTimestamp) {
        return Minutes.minutesBetween(parseDateTime(olderTimestamp), parseDateTime(newerTimestamp)).getMinutes();
    }

    public static Integer minutesBetween(LocalDate older, LocalDate newer) {
        return Minutes.minutesBetween(older, newer).getMinutes();
    }

    public static Integer hoursBetween(long olderTimestamp, long newerTimestamp) {
        return Hours.hoursBetween(parseDateTime(olderTimestamp), parseDateTime(newerTimestamp)).getHours();
    }

    public static Integer hoursBetween(LocalDate older, LocalDate newer) {
        return Hours.hoursBetween(older, newer).getHours();
    }

    public static Integer daysBetween(long olderTimestamp, long newerTimestamp) {
        return Days.daysBetween(parseDateTime(olderTimestamp), parseDateTime(newerTimestamp)).getDays();
    }

    public static Integer daysBetween(LocalDate creationDate, LocalDate expireDate) {
        return Days.daysBetween(creationDate, expireDate).getDays();
    }

    public static DateTime parseDateTime(long timemillis) {
        return new DateTime(timemillis);
    }

    public static long getStartOfThisWeek(int week) {
        Calendar c = Calendar.getInstance();
        final int weekOfYear = c.get(Calendar.WEEK_OF_YEAR);
        if (week != weekOfYear) {
            c.add(Calendar.WEEK_OF_YEAR, week - weekOfYear);
        }
        int day_of_week = c.get(Calendar.DAY_OF_WEEK) - 1;
        if (day_of_week == 0)
            day_of_week = 7;
        c.add(Calendar.DATE, -day_of_week + 1);
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        return c.getTime().getTime();
    }

    public static long getEndOfThisWeek(int week) {
        Calendar c = Calendar.getInstance();
        final int weekOfYear = c.get(Calendar.WEEK_OF_YEAR);
        if (week != weekOfYear) {
            c.add(Calendar.WEEK_OF_YEAR, week - weekOfYear);
        }
        int day_of_week = c.get(Calendar.DAY_OF_WEEK) - 1;
        if (day_of_week == 0)
            day_of_week = 7;
        c.add(Calendar.DATE, -day_of_week + 7);
        c.set(Calendar.HOUR_OF_DAY, 23);
        c.set(Calendar.MINUTE, 59);
        c.set(Calendar.SECOND, 59);
        c.set(Calendar.MILLISECOND, 999);
        return c.getTime().getTime();
    }

    public static long getStartOfThisWeek(int year, int week) {
        Calendar c = Calendar.getInstance();
        c.set(Calendar.YEAR, year);
        c.set(Calendar.WEEK_OF_YEAR, week);
        int day_of_week = c.get(Calendar.DAY_OF_WEEK) - 1;
        if (day_of_week == 0)
            day_of_week = 7;
        c.add(Calendar.DATE, -day_of_week + 1);
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        return c.getTime().getTime();
    }

    public static long getEndOfThisWeek(int year, int week) {
        Calendar c = Calendar.getInstance();
        c.set(Calendar.YEAR, year);
        c.set(Calendar.WEEK_OF_YEAR, week);
        int day_of_week = c.get(Calendar.DAY_OF_WEEK) - 1;
        if (day_of_week == 0)
            day_of_week = 7;
        c.add(Calendar.DATE, -day_of_week + 7);
        c.set(Calendar.HOUR_OF_DAY, 23);
        c.set(Calendar.MINUTE, 59);
        c.set(Calendar.SECOND, 59);
        c.set(Calendar.MILLISECOND, 999);
        return c.getTime().getTime();
    }

    public static long getStartOfThisWeek() {
        Calendar c = Calendar.getInstance();
        int day_of_week = c.get(Calendar.DAY_OF_WEEK) - 1;
        if (day_of_week == 0)
            day_of_week = 7;
        c.add(Calendar.DATE, -day_of_week + 1);
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        return c.getTime().getTime();
    }

    public static long getEndOfThisWeek() {
        Calendar c = Calendar.getInstance();
        int day_of_week = c.get(Calendar.DAY_OF_WEEK) - 1;
        if (day_of_week == 0)
            day_of_week = 7;
        c.add(Calendar.DATE, -day_of_week + 7);
        c.set(Calendar.HOUR_OF_DAY, 23);
        c.set(Calendar.MINUTE, 59);
        c.set(Calendar.SECOND, 59);
        c.set(Calendar.MILLISECOND, 999);
        return c.getTime().getTime();
    }

    public static long getStartOfLastWeek() {
        Calendar c = Calendar.getInstance();
        int day_of_week = c.get(Calendar.DAY_OF_WEEK) - 1;
        if (day_of_week == 0)
            day_of_week = 7;
        c.add(Calendar.DATE, -day_of_week + 1);
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        c.add(Calendar.WEEK_OF_YEAR, -1);
        return c.getTime().getTime();
    }

    public static long getEndOfLastWeek() {
        Calendar c = Calendar.getInstance();
        int day_of_week = c.get(Calendar.DAY_OF_WEEK) - 1;
        if (day_of_week == 0)
            day_of_week = 7;
        c.add(Calendar.DATE, -day_of_week + 7);
        c.set(Calendar.HOUR_OF_DAY, 23);
        c.set(Calendar.MINUTE, 59);
        c.set(Calendar.SECOND, 59);
        c.set(Calendar.MILLISECOND, 999);
        c.add(Calendar.WEEK_OF_YEAR, -1);
        return c.getTime().getTime();
    }

    public static long getStartOfThisMonth() {
        Calendar c = Calendar.getInstance();
        c.set(Calendar.DAY_OF_MONTH, 1);
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        return c.getTime().getTime();
    }

    public static long getEndOfThisMonth() {
        Calendar c = Calendar.getInstance();
        c.add(Calendar.MONTH, 1);
        c.set(Calendar.DAY_OF_MONTH, 1);
        c.add(Calendar.DAY_OF_MONTH, -1);
        c.set(Calendar.HOUR_OF_DAY, 23);
        c.set(Calendar.MINUTE, 59);
        c.set(Calendar.SECOND, 59);
        c.set(Calendar.MILLISECOND, 999);
        return c.getTime().getTime();
    }

    public static long getStartOfLastMonth() {
        Calendar c = Calendar.getInstance();
        c.set(Calendar.DAY_OF_MONTH, 1);
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        c.add(Calendar.MONTH, -1);
        return c.getTime().getTime();
    }

    public static long getEndOfLastMonth() {
        Calendar c = Calendar.getInstance();
        c.add(Calendar.MONTH, 1);
        c.set(Calendar.DAY_OF_MONTH, 1);
        c.add(Calendar.DAY_OF_MONTH, -1);
        c.set(Calendar.HOUR_OF_DAY, 23);
        c.set(Calendar.MINUTE, 59);
        c.set(Calendar.SECOND, 59);
        c.set(Calendar.MILLISECOND, 999);
        c.add(Calendar.MONTH, -1);
        return c.getTime().getTime();
    }

    public static long getStartOfToday() {
        final Calendar c = Calendar.getInstance();
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        return c.getTime().getTime();
    }

    public static long getEndOfToday() {
        final Calendar c = Calendar.getInstance();
        c.set(Calendar.HOUR_OF_DAY, 23);
        c.set(Calendar.MINUTE, 59);
        c.set(Calendar.SECOND, 59);
        c.set(Calendar.MILLISECOND, 999);
        return c.getTime().getTime();
    }

    public static long getStartOfYesterday() {
        final Calendar c = Calendar.getInstance();
        c.add(Calendar.DAY_OF_YEAR, -1);
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        return c.getTime().getTime();
    }

    public static long getEndOfYesterday() {
        final Calendar c = Calendar.getInstance();
        c.add(Calendar.DAY_OF_YEAR, -1);
        c.set(Calendar.HOUR_OF_DAY, 23);
        c.set(Calendar.MINUTE, 59);
        c.set(Calendar.SECOND, 59);
        c.set(Calendar.MILLISECOND, 999);
        return c.getTime().getTime();
    }

    public static long getStartOfThisHour() {
        final Calendar c = Calendar.getInstance();
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        return c.getTime().getTime();
    }

    public static long getEndOfThisHour() {
        final Calendar c = Calendar.getInstance();
        c.set(Calendar.MINUTE, 59);
        c.set(Calendar.SECOND, 59);
        c.set(Calendar.MILLISECOND, 999);
        return c.getTime().getTime();
    }

    public static long getStartOfThisHalfHour() {
        final Calendar c = Calendar.getInstance();
        final int currentMinute = c.get(Calendar.MINUTE);
        c.set(Calendar.MINUTE, currentMinute < 30 ? 0 : 30);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        return c.getTime().getTime();
    }

    public static long getEndOfThisHalfHour() {
        final Calendar c = Calendar.getInstance();
        final int currentMinute = c.get(Calendar.MINUTE);
        c.set(Calendar.MINUTE, currentMinute < 30 ? 29 : 59);
        c.set(Calendar.SECOND, 59);
        c.set(Calendar.MILLISECOND, 999);
        return c.getTime().getTime();
    }

    public static long getStartOfLastHour() {
        final Calendar c = Calendar.getInstance();
        c.add(Calendar.HOUR_OF_DAY, -1);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        return c.getTime().getTime();
    }

    public static long getEndOfLastHour() {
        final Calendar c = Calendar.getInstance();
        c.add(Calendar.HOUR_OF_DAY, -1);
        c.set(Calendar.MINUTE, 59);
        c.set(Calendar.SECOND, 59);
        c.set(Calendar.MILLISECOND, 999);
        return c.getTime().getTime();
    }

    public static long getStartOfEverDay(String yyyyMMdd, String pattern) {
        yyyyMMdd = yyyyMMdd + " 00:00:00";
        SimpleDateFormat sdf = new SimpleDateFormat(pattern + " HH:mm:ss");
        try {
            return sdf.parse(yyyyMMdd).getTime();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return 0;
    }

    public static long getStartOfEverDay(String yyyyMMdd) {
        return getStartOfEverDay(yyyyMMdd, "yyyy-MM-dd");
    }

    public static long getEndOfEverDay(String yyyyMMdd, String pattern) {
        yyyyMMdd = yyyyMMdd + " 23:59:59";
        SimpleDateFormat sdf = new SimpleDateFormat(pattern + " HH:mm:ss");
        try {
            return sdf.parse(yyyyMMdd).getTime();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return 0;
    }

    public static long getEndOfEverDay(String yyyyMMdd) {
        return getEndOfEverDay(yyyyMMdd, "yyyy-MM-dd");
    }

    public static long getStartOfEverDay(long timestamp) {
        String yyyyMMdd = timeLongToString(timestamp, "yyyy-MM-dd") + " 00:00:00";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            return sdf.parse(yyyyMMdd).getTime();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return 0;
    }

    public static long getEndOfEverDay(long timestamp) {
        String yyyyMMdd = timeLongToString(timestamp, "yyyy-MM-dd") + " 23:59:59";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            return sdf.parse(yyyyMMdd).getTime();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return 0;
    }

    public static long getStartOfEverMonth(int year, int month) {
        Calendar c = Calendar.getInstance();
        c.set(Calendar.YEAR, year);
        c.set(Calendar.MONTH, month - 1);
        c.set(Calendar.DAY_OF_MONTH, 1);
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        return c.getTime().getTime();
    }

    public static long getEndOfEverMonth(int year, int month) {
        return getStartOfEverMonth(year, month + 1) - 1;
    }

    public static long getStartOfEverYear(int year) {
        Calendar c = Calendar.getInstance();
        c.set(Calendar.YEAR, year);
        c.set(Calendar.MONTH, 0);
        c.set(Calendar.DAY_OF_MONTH, 1);
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        return c.getTime().getTime();
    }

    public static long getEndOfEverYear(int year) {
        return getStartOfEverYear(year + 1) - 1;
    }

    public static String formatTime(int seconds) {
        StringBuilder result = new StringBuilder();
        int hours = seconds / 3600;
        if (hours > 0) result.append(hours).append("时");
        int min = seconds % 3600 / 60;
        if (min > 0) result.append(min).append("分");
        int sec = seconds % 60;
        if (sec > 0) result.append(sec).append("秒");
        if (hours == 0 && min == 0 && sec == 0) return "0时";
        return result.toString();
    }

    /**
     * 当时时间变动时间
     *
     * @param type   Calendar.YEAR，Calendar.MONTH，Calendar.DATE，Calendar.HOUR，Calendar.MINUTE，Calendar.SECOND
     * @param offset 变动值
     * @return 新的时间戳
     */
    public static long currentTimestampOffset(int type, int offset) {
        return timestampAddOrSub(getTimestamp(), offset < 0, type, Math.abs(offset));
    }

    /**
     * 时间戳添加
     *
     * @param timestamp 时间戳
     * @param type      Calendar.YEAR，Calendar.MONTH，Calendar.DATE，Calendar.HOUR，Calendar.MINUTE，Calendar.SECOND
     * @param offset    变动值
     * @return 新的时间戳
     */
    public static long timestampAdd(long timestamp, int type, int offset) {
        return timestampAddOrSub(timestamp, false, type, offset);
    }

    /**
     * 时间戳减少
     *
     * @param timestamp 时间戳
     * @param type      Calendar.YEAR，Calendar.MONTH，Calendar.DATE，Calendar.HOUR，Calendar.MINUTE，Calendar.SECOND
     * @param offset    变动值
     * @return 新的时间戳
     */
    public static long timestampSub(long timestamp, int type, int offset) {
        return timestampAddOrSub(timestamp, true, type, offset);
    }

    /**
     * @param timestamp 时间戳
     * @param sub       是否做减法
     * @param type      Calendar.YEAR，Calendar.MONTH，Calendar.DATE，Calendar.HOUR，Calendar.MINUTE，Calendar.SECOND
     * @param offset    变动值
     * @return 新的时间戳
     */
    public static long timestampAddOrSub(long timestamp, boolean sub, int type, int offset) {
        Calendar c = Calendar.getInstance();
        c.setTimeInMillis(timestamp);
        c.add(type, sub ? -offset : offset);
        return c.getTime().getTime();
    }

    /**
     * @param currentTimeMillisOfToday 当天的时刻时间戳
     * @param startHourTime            小时开始时间，例如："14:56:30"
     * @param endHourTime              小时结束时间，例如："14:56:30"
     * @return 是否处于当天的某个小时时间段范围内
     */
    public static boolean isBetweenHourTime(long currentTimeMillisOfToday, String startHourTime, String endHourTime) {
        final String[] startTimes = startHourTime.split(":");
        final String[] endTimes = endHourTime.split(":");
        if (startTimes.length == 3 && endTimes.length == 3) {
            long startTimeMillis = Integer.parseInt(startTimes[0]) * 60 * 60 * 1000L + Integer.parseInt(startTimes[1]) * 60 * 1000L + Integer.parseInt(startTimes[2]) * 1000L;
            long endTimeMillis = Integer.parseInt(endTimes[0]) * 60 * 60 * 1000L + Integer.parseInt(endTimes[1]) * 60 * 1000L + Integer.parseInt(endTimes[2]) * 1000L;
            return currentTimeMillisOfToday >= startTimeMillis && currentTimeMillisOfToday <= endTimeMillis;
        }
        return false;
    }

    /**
     * @param currentTimeMillisOfToday 当天的时刻时间戳
     * @param hourTime                 小时开始时间，例如："14:56:30"
     * @return 是否大于当天的某个小时时间段范围内
     */
    public static boolean isGreaterThanHourTime(long currentTimeMillisOfToday, String hourTime) {
        final String[] startTimes = hourTime.split(":");
        if (startTimes.length == 3) {
            long startTimeMillis = Integer.parseInt(startTimes[0]) * 60 * 60 * 1000L + Integer.parseInt(startTimes[1]) * 60 * 1000L + Integer.parseInt(startTimes[2]) * 1000L;
            return currentTimeMillisOfToday >= startTimeMillis;
        }
        return false;
    }

    /**
     * @param currentTimeMillisOfToday 当天的时刻时间戳
     * @param hourTime                 小时时间，例如："14:56:30"
     * @return 是否小于当天的某个小时时间段范围内
     */
    public static boolean isLessThanHourTime(long currentTimeMillisOfToday, String hourTime) {
        final String[] startTimes = hourTime.split(":");
        if (startTimes.length == 3) {
            long startTimeMillis = Integer.parseInt(startTimes[0]) * 60 * 60 * 1000L + Integer.parseInt(startTimes[1]) * 60 * 1000L + Integer.parseInt(startTimes[2]) * 1000L;
            return currentTimeMillisOfToday <= startTimeMillis;
        }
        return false;
    }
}
